package com.a1anwang.okble.server.core;

import com.a1anwang.okble.common.BLEOperationQueue;
import com.a1anwang.okble.common.CommonUUIDUtils;
import com.a1anwang.okble.common.LogUtils;
import com.a1anwang.okble.common.OKBLECharacteristicModel;
import com.a1anwang.okble.common.OKBLEDataUtils;
import com.a1anwang.okble.common.OKBLEServiceModel;

import ohos.app.Context;
import ohos.bluetooth.ble.*;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import java.util.List;
import java.util.UUID;

import static com.a1anwang.okble.server.core.OKBLEServerOperation.BLEServerOperationListener.Operation_FAILED_BLE_Failed;
import static com.a1anwang.okble.server.core.OKBLEServerOperation.BLEServerOperationListener.Operation_FAILED_Invalid_Characteristic_UUID;
import static com.a1anwang.okble.server.core.OKBLEServerOperation.BLEServerOperationListener.Operation_FAILED_Invalid_Service_UUID;

/**
 * Created by a1anwang.com on 2018/5/30.
 */
public class OKBLEServerDeviceImp implements OKBLEServerDevice {
    private EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());
    private static final int OperationInterval = 50;
    private BLEOperationQueue<OKBLEServerOperation> bleOperationQueue;
    private Context context;
    private BlePeripheralManager gattServer;
    /**
     * 构造函数
     *
     * @param context 上下文
     */
    public OKBLEServerDeviceImp(Context context) {
        this.context = context;
        bleOperationQueue = new BLEOperationQueue<>();
    }

    private void openGattServer() {
        gattServer = new BlePeripheralManager(context, gattServerCallback, 0);
    }

    @Override
    public void reSet() {
        if (gattServer != null) {
            gattServer.clearServices();
            gattServer.close();
        }
        gattServer = null;
    }

    @Override
    public void addCharacteristic(List<OKBLECharacteristicModel> okbleCharacteristicModels, OKBLEServiceModel okbleServiceModel, OKBLEServerOperation.BLEServerOperationListener operationListener) {
        if (gattServer == null) {
            openGattServer();
        }
        if (!OKBLEDataUtils.isValidUUID(okbleServiceModel.getUuid())) {
            if (operationListener != null) {
                operationListener.onAddCharacteristicFailed(Operation_FAILED_Invalid_Service_UUID, OKBLEServerOperationFailedDescUtils.getDesc(Operation_FAILED_Invalid_Service_UUID));
            }
            return;
        }
        for (OKBLECharacteristicModel okbleCharacteristicModel : okbleCharacteristicModels) {
            if (!OKBLEDataUtils.isValidUUID(okbleCharacteristicModel.getUuid())) {
                if (operationListener != null) {
                    operationListener.onAddCharacteristicFailed(Operation_FAILED_Invalid_Characteristic_UUID, OKBLEServerOperationFailedDescUtils.getDesc(Operation_FAILED_Invalid_Characteristic_UUID));
                }
                return;
            }
        }

        OKBLEServerOperation operation = new OKBLEServerOperation();
        operation.characteristicModels = okbleCharacteristicModels;
        operation.serviceModel = okbleServiceModel;
        operation.operationListener = operationListener;

        bleOperationQueue.add(operation);
        checkNextBleOperation();
    }

    synchronized private void checkNextBleOperation() {
        if (bleOperationQueue.getOperationSize() == 1) {
            OKBLEServerOperation bleOperation = bleOperationQueue.getFirst();
            doBleOperation(bleOperation);
        }
    }

    synchronized private void doBleOperation(OKBLEServerOperation bleOperation) {

        GattService bluetoothGattService;

        bluetoothGattService = new GattService(UUID.fromString(bleOperation.serviceModel.getUuid()), true);

        for (OKBLECharacteristicModel characteristicModel : bleOperation.characteristicModels) {
            GattCharacteristic characteristic = createCharacteristic(characteristicModel);
            bluetoothGattService.addCharacteristic(characteristic);
        }
        gattServer.addService(bluetoothGattService);
    }

    private GattCharacteristic createCharacteristic(OKBLECharacteristicModel characteristicModel) {
        int properties = 0;
        int permissions = 0;
        if (characteristicModel.isCanIndicate()) {
            properties |= 32;
        }
        if (characteristicModel.isCanNotify()) {
            properties |= 16;
        }
        if (characteristicModel.isCanRead()) {
            properties |= GattCharacteristic.PROPERTY_READ;
            permissions |= 1;
        }
        if (characteristicModel.isCanWrite()) {
            properties |= GattCharacteristic.PROPERTY_WRITE;
        }
        if (characteristicModel.isCanWriteNoResponse()) {
            properties |= GattCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
        }

        if (characteristicModel.isCanWrite() || characteristicModel.isCanWriteNoResponse()) {
            permissions |= 16;
        }

        GattCharacteristic characteristic = new GattCharacteristic(
                UUID.fromString(characteristicModel.getUuid()), properties, permissions);
        if (characteristicModel.isCanIndicate() || characteristicModel.isCanNotify()) {
            characteristic.addDescriptor(new GattDescriptor(UUID.fromString(CommonUUIDUtils.Client_Characteristic_Configuration), 16));
        }
        return characteristic;
    }

    private BlePeripheralManagerCallback gattServerCallback = new BlePeripheralManagerCallback() {

        @Override
        public void connectionStateChangeEvent(BlePeripheralDevice device, int interval, int latency, int timeout, int status) {
            super.connectionStateChangeEvent(device, interval, latency, timeout, status);
            LogUtils.e("---- server onConnectionStateChange status:" + status);
        }

        @Override
        public void serviceAddedEvent(int status, GattService service) {
            super.serviceAddedEvent(status, service);
            LogUtils.e("---- onServiceAdded---- ");
            for (GattCharacteristic characteristic : service.getCharacteristics()) {
                LogUtils.e("---- characteristic:" + characteristic.getUuid().toString());
            }
            if (bleOperationQueue.getOperationSize() > 0) {
                OKBLEServerOperation operation = bleOperationQueue.removeFirst();
                if (operation != null && operation.operationListener != null) {
                    if (status == 0) {
                        operation.operationListener.onAddCharacteristicSuccess();

                    } else {
                        operation.operationListener.onAddCharacteristicFailed(Operation_FAILED_BLE_Failed, OKBLEServerOperationFailedDescUtils.getDesc(status));
                    }
                }
                handler.removeTask(nextRunnable);
                handler.postTask(nextRunnable, OperationInterval);
            }
        }

        @Override
        public void receiveCharacteristicReadEvent(BlePeripheralDevice device, int transId, int offset, GattCharacteristic characteristic) {
            super.receiveCharacteristicReadEvent(device, transId, offset, characteristic);
        }

        @Override
        public void receiveCharacteristicWriteEvent(BlePeripheralDevice device, int transId, GattCharacteristic characteristic, boolean isPrep, boolean needRsp, int offset, byte[] value) {
            super.receiveCharacteristicWriteEvent(device, transId, characteristic, isPrep, needRsp, offset, value);
        }

        @Override
        public void receiveDescriptorReadEvent(BlePeripheralDevice device, int transId, int offset, GattDescriptor descriptor) {
            super.receiveDescriptorReadEvent(device, transId, offset, descriptor);
        }

        @Override
        public void receiveDescriptorWriteRequestEvent(BlePeripheralDevice device, int transId, GattDescriptor descriptor, boolean isPrep, boolean needRsp, int offset, byte[] value) {
            super.receiveDescriptorWriteRequestEvent(device, transId, descriptor, isPrep, needRsp, offset, value);
        }

        @Override
        public void executeWriteEvent(BlePeripheralDevice device, int transId, boolean execute) {
            super.executeWriteEvent(device, transId, execute);
        }

        @Override
        public void mtuUpdateEvent(BlePeripheralDevice device, int mtu) {
            super.mtuUpdateEvent(device, mtu);
        }

        @Override
        public void notificationSentEvent(BlePeripheralDevice device, int status) {
            super.notificationSentEvent(device, status);
        }
    };

    Runnable nextRunnable = new Runnable() {
        @Override
        public void run() {
            if (bleOperationQueue.getOperationSize() > 0) {
                doNextBleOperation();
            }
        }
    };

    private void doNextBleOperation() {
        if (bleOperationQueue.getOperationSize() > 0) {
            OKBLEServerOperation okbleServerOperation = bleOperationQueue.getFirst();
            doBleOperation(okbleServerOperation);
        }
    }

}
